package com.carey.blog.repository.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import com.carey.blog.framework.latke.Keys;
import com.carey.blog.framework.latke.repository.RepositoryException;
import com.carey.blog.framework.latke.repository.gae.AbstractGAERepository;
import com.carey.blog.model.Tag;
import com.carey.blog.repository.TagRepository;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.QueryResultIterable;

/**
 * Tag Google App Engine repository.
 * 
 */
public final class TagGAERepository extends AbstractGAERepository implements
		TagRepository {

	/**
	 * Logger.
	 */
	private static final Logger LOGGER = Logger
			.getLogger(TagGAERepository.class.getName());
	/**
	 * Tag-Article relation repository.
	 */
	private TagArticleGAERepository tagArticleRepository = TagArticleGAERepository
			.getInstance();
	/**
	 * Key of the most used tag cache count.
	 */
	private static final String KEY_MOST_USED_TAG_CACHE_CNT = "mostUsedTagCacheCnt";

	@Override
	public String getName() {
		return Tag.TAG;
	}

	@Override
	public JSONObject getByTitle(final String tagTitle)
			throws RepositoryException {
		final String cacheKey = "getByTitle[" + tagTitle + "]";
		JSONObject ret = (JSONObject) CACHE.get(cacheKey);

		if (null == ret) {
			final Query query = new Query(Tag.TAG);
			query.addFilter(Tag.TAG_TITLE, Query.FilterOperator.EQUAL, tagTitle);
			final PreparedQuery preparedQuery = getDatastoreService().prepare(
					query);
			final Entity entity = preparedQuery.asSingleEntity();
			if (null == entity) {
				return null;
			}

			ret = entity2JSONObject(entity);

			CACHE.put(cacheKey, ret);
		}

		return ret;
	}

	@Override
	public List<JSONObject> getMostUsedTags(final int num) {
		final String cacheKey = KEY_MOST_USED_TAG_CACHE_CNT + "[" + num + "]";
		@SuppressWarnings("unchecked")
		List<JSONObject> ret = (List<JSONObject>) CACHE.get(cacheKey);
		if (null != ret) {
			LOGGER.log(Level.FINEST, "Got the most used tags from cache");
		} else {
			ret = new ArrayList<JSONObject>();

			final Query query = new Query(getName());
			query.addSort(Tag.TAG_PUBLISHED_REFERENCE_COUNT,
					Query.SortDirection.DESCENDING);
			final PreparedQuery preparedQuery = getDatastoreService().prepare(
					query);
			final QueryResultIterable<Entity> queryResultIterable = preparedQuery
					.asQueryResultIterable(FetchOptions.Builder.withLimit(num));

			for (final Entity entity : queryResultIterable) {
				final JSONObject tag = entity2JSONObject(entity);
				ret.add(tag);
			}

			CACHE.put(cacheKey, ret);

			LOGGER.log(Level.FINEST,
					"Got the most used tags, then put it into cache");
		}

		return ret;
	}

	@Override
	public List<JSONObject> getByArticleId(final String articleId)
			throws RepositoryException {
		final List<JSONObject> ret = new ArrayList<JSONObject>();

		try {
			final List<JSONObject> tagArticleRelations = tagArticleRepository
					.getByArticleId(articleId);
			for (final JSONObject tagArticleRelation : tagArticleRelations) {
				final String tagId = tagArticleRelation.getString(Tag.TAG + "_"
						+ Keys.OBJECT_ID);
				final JSONObject tag = get(tagId);

				ret.add(tag);
			}
		} catch (final JSONException e) {
			LOGGER.log(Level.SEVERE, e.getMessage(), e);
			throw new RepositoryException(e);
		}

		return ret;
	}

	/**
	 * Gets the {@link TagGAERepository} singleton.
	 * 
	 * @return the singleton
	 */
	public static TagGAERepository getInstance() {
		return SingletonHolder.SINGLETON;
	}

	/**
	 * Private default constructor.
	 */
	private TagGAERepository() {
	}

	/**
	 * Singleton holder.
	 * 
	 */
	private static final class SingletonHolder {

		/**
		 * Singleton.
		 */
		private static final TagGAERepository SINGLETON = new TagGAERepository();

		/**
		 * Private default constructor.
		 */
		private SingletonHolder() {
		}
	}
}
